package javavis.jip2d.functions;

import javavis.base.JIPException;
import javavis.base.parameter.JIPParamFloat;
import javavis.base.parameter.JIPParamImage;
import javavis.base.parameter.JIPParamList;
import javavis.jip2d.base.FunctionGroup;
import javavis.jip2d.base.JIPFunction;
import javavis.jip2d.base.JIPImage;
import javavis.jip2d.base.bitmaps.JIPBmpBit;
import javavis.jip2d.base.bitmaps.JIPBmpColor;
import javavis.jip2d.base.bitmaps.JIPImgBitmap;
import javavis.jip2d.base.geometrics.JIPImgGeometric;

/**
 * @author dviejo
 * Performs a convolution for an image using the information from another image as convolution mask.<BR>
 * It is applicable for: BYTE, WORD, COLOR and REAL types.<BR>
 * <ul><B>Input parameters:</B><BR>
 * <li>img: Input image<BR>
 * <li>image: Image to use in convolution<BR>
 * <li>mult: Multiplier<BR>
 * <li>div: Divisor <BR>
 * <li>method: Method to treat the borders. 
 *	PAD->The first row is duplicated so that the -1 row is the same. The same for last row and first and last columns. 
 *	ZERO->border pixels are marked as 0.<BR><BR>
 * </ul>
 * <ul><B>Output parameters:</B><BR>
 * <li>Convolutioned image with the input image, with type the same as the input image.<BR><BR>
 * </ul>
 */

public class FConvolveImage extends JIPFunction {
	private static final long serialVersionUID = -6863560219268735784L;

	/**
	 * @uml.property  name="mask" multiplicity="(0 -1)" dimension="1"
	 */
	double []mask;

	public FConvolveImage() {
		super();
		name = "FConvolveImage";
		description = "Convolution of two images";
		groupFunc = FunctionGroup.Convolution;

		JIPParamImage p1 = new JIPParamImage("image", true, true);
		p1.setDescription("Image for convolution");
		JIPParamFloat p2 = new JIPParamFloat("mult", false, true);
		p2.setDefault(1.0f);
		p2.setDescription("Multiplier");
		JIPParamFloat p3 = new JIPParamFloat("div", false, true);
		p3.setDefault(1.0f);
		p3.setDescription("Divisor");
		JIPParamList p4 = new JIPParamList("method", false, true);
		String []paux = new String[2];
		paux[0]="PAD";
		paux[1]="ZERO";
		p4.setDefault(paux);
		p4.setDescription("Method to process edges");

		addParam(p1);
		addParam(p2);
		addParam(p3);
		addParam(p4);
	}

	public JIPImage processImg(JIPImage img) throws JIPException {
		if (img instanceof JIPImgGeometric || img instanceof JIPBmpBit) 
			throw new JIPException("ConvolveImage can not be applied to this image type");
		JIPImage convo = getParamValueImg("image");
		if (convo instanceof JIPImgGeometric) 
			throw new JIPException("ConvolveImage can not be applied with this image type");
		float mult = getParamValueFloat("mult");
		float div = getParamValueFloat("div");
		String p4 = getParamValueString("method");
		JIPImgBitmap imgBmp = (JIPImgBitmap)img;


		//////////////////////////////
		int maskWidth, maskHeight;
		double[] bitmap;
		int width, height; //input image dimensions
		boolean ZERO = false;
		int radiusW;
		int radiusH;
		int oddW;
		int oddH;

		int cont;
		int row, col;
		width = img.getWidth();
		height = img.getHeight();
		maskWidth = convo.getWidth();
		maskHeight = convo.getHeight();
		radiusW = (maskWidth)/2;
		radiusH = (maskHeight)/2;
		oddW = maskWidth%2;
		oddH = maskHeight%2;
		int numBands = imgBmp.getNumBands();
		double []result = new double[width*height];
		JIPImgBitmap res = (JIPImgBitmap)JIPImage.newImage(numBands, width, height, imgBmp.getType());
		double ratio = mult / div;

		int i, j, pos;
		int newrow, newcol;
		double []A;
		
		if (p4.equals("ZERO")) ZERO = true;
		if (convo instanceof JIPBmpColor) {
			JIPFunction ctg = new FColorToGray();
			JIPImage auxImg = ctg.processImg(convo);
			mask = ((JIPImgBitmap)auxImg).getAllPixels();
		}
		else
			mask = ((JIPImgBitmap)convo).getAllPixels();
		
		for (int nb = 0; nb < numBands; nb++) {
			bitmap = imgBmp.getAllPixels(nb);
			for(cont=0;cont<bitmap.length;cont++)
			{
				row = cont / width;
				col = cont % width;
				if(ZERO && (row<radiusH || row>height-radiusH || col<radiusW || col>width-radiusW))
					result[cont] = 0;
				else
				{
					A = new double[mask.length];
					
					pos = 0;
					for(i=row - radiusH; i<row + radiusH + oddH; i++)
					{
						if(i<0)
							newrow = 0;
						else if(i>=height)
							newrow = (height-1)*width;
						else
							newrow = i*width;
						for(j=col-radiusW; j<col+radiusW + oddW; j++)
						{
							if(j<0)
								newcol = 0;
							else if(j>=width)
								newcol = width -1;
							else newcol = j;
							
							A[pos] = bitmap[newrow + newcol];
							pos++;
						}
					}
					result[cont] = reduce(A) * ratio;
				}
				
				
			}
			res.setAllPixels(nb, result);
		}
		/////////////////////////////
		return res;
	}
	
	private double reduce(double []A) 
	{
		double result = 0;
		int cont;
		
		for(cont=0;cont<A.length;cont++)
		{
			result += A[cont] * mask[cont];
		}
		return result;
	}
}
